import sys, os

sys.path.append("./lib")

import yaml, zipimport, helpers

class Workflow(object):
    def __init__(self, filename):
        try:
            base_name = filename.split(os.sep)[-1].split(".")[0]
            workflow_file = zipimport.zipimporter(filename)
            self.__module = workflow_file.load_module(base_name)
            yamlDoc = yaml.load(workflow_file.get_data(base_name + ".yml"))
            if yamlDoc['inputs'] is not None:
                self.__inputs = yamlDoc['inputs'].keys()
                self.__sizes = yamlDoc['inputs']
                [int(i) for i in self.__sizes.values() if i is not None]
            else:
                self.__inputs = []
                self.__sizes = []
            self.__operations = yamlDoc['operations']
            self.__title = yamlDoc['title']
            self.__mode = yamlDoc['mode']
            try:
                self.__description = yamlDoc['description']
            except Exception:
                self.__description = ""
        except Exception:
            raise CWWorkflowError()
    
    # Workflow Properties
    @property
    def title(self):
        return self.__title
    @property
    def operations(self):
        return self.__operations
    @property
    def operationNames(self):
        names = {}
        try:
            for i in self.__operations:
                try:
                    docstringlist = getattr(self.__module, i['name']).__doc__.split("\n")
                
                    for line in docstringlist:
                        line = line.strip()
                        if line != "":
                            names[i['name']] = line
                            break
                except Exception:
                    names[i['name']] = i['name']
        except AttributeError:
            raise CWWorkflowError()
        return names
    @property
    def inputs(self):
        return self.__inputs
    @property
    def sizes(self):
        return self.__sizes
    @property
    def description(self):
        return self.__description
    @property
    def unit(self):
        return {'alpha': 'characters',
                'binary': 'bits'}[self.__mode]

    def steps(self, inputs):
        """
        Runs the workflow step-by-step. Provides an iterator.

        Input: A dictionary of inputs.
        Output: A dictionary of outputs.
        """
        workflow_values = {}
        for k,v in inputs.items():
            try:
                workflow_values[k] = helpers.testValues[self.__mode](v, self.__sizes[k])
            except ValueError, (message):
                raise ValueError("Invalid input, %s %s." % (k, message))
        module = self.__module
        for op in self.__operations:
            results = {}
            try:
                if op['inputs'] is not None:
                    inputs = [workflow_values[i] for i in op['inputs']]
                    operation_output = module.__dict__[op['name']](*inputs)
                else:
                    operation_output = module.__dict__[op['name']]()
                if type(operation_output) is type(()):
                    for i in xrange(len(operation_output)):
                        workflow_values[op['outputs'][i]] = operation_output[i]
                        results[op['outputs'][i]] = operation_output[i]
                else:
                    workflow_values[op['outputs'][0]] = operation_output
                    results[op['outputs'][0]] = operation_output
            except Exception:
                raise CWWorkflowError()
            yield results

class CWWorkflowError(Exception):
    def __init__(self, reason="There is an error in the file format."):
        self.parameter = reason
    def __str__(self):
        return repr(self.parameter)
        